package marcis.jarina.directory;

import marcis.jarina.directory.repository.FasterJarProcessor;
import marcis.jarina.directory.repository.JarVersion;
import org.apache.commons.collections.Predicate;
import org.apache.log4j.Logger;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;


/**
 * Message producer for: PomProcessor, SourceProcessor and JarProcessor
 *
 * @author jan.marcis
 */
public class DirectoryWalker implements DirectoryService {

    private static final Logger log = Logger.getLogger(DirectoryWalker.class);

    private File directory;

    private final FilenameFilter filter;

    private boolean skipOlderDirs = true;

    private final Collection<FileActionListener> listeners = new ArrayList<FileActionListener>();

    public DirectoryWalker(File startDir, FilenameFilter myfilter) {
        this.directory = startDir;
        this.filter = myfilter;
    }

    @Override
    public File getDirectory() {
        return directory;
    }

    @Override
    public void onFileFound(File foundFile) {
        for (FileActionListener listener : listeners) {
            listener.onFileFound(foundFile);
        }
    }

    @Override
    public File getRepositoryDir() {
        return FasterJarProcessor.findRepository();
    }

    @Override
    public void finish() {
        //TODO is there anything to impl?
        for (FileActionListener listener : listeners) {
            listener.finish();
        }
    }

    @Override
    public void setDirectory(File directory) {
        this.directory = directory;
    }

    @Override
    public void doSearch() {
        checkState();
        processOneDir(directory);
    }

    private void checkState() {
        if (directory == null) {
            throw new NullPointerException("Directory must be set!");
        }
        if (listeners.isEmpty()) {
            log.debug("No one is listening!");
        }
    }

    // asi: splnuje podminky filtru?
    private boolean isFileOk(File file) {
        File parent = file.getParentFile();
        List<File> list = Arrays.asList(parent.listFiles(filter));
        return list.contains(file);
    }

    private boolean isLastVersionDir(File dir) {
        if (dir == null || !dir.isDirectory()) {
            return false;
        }
        File parent = dir.getParentFile();
        Predicate predicate = new MyFileFilter.PredicateFileType(MyFileFilter.PredicateFileType.FILE_TYPE.DIRECTORY);
        MyFileFilter fileFilter = new MyFileFilter(predicate);
        File[] dirs = parent.listFiles(fileFilter);
        double maxVersion = 0;
        File maxVersionFile = null;
        for (File versionedDir : dirs) {
            JarVersion version = new JarVersion(versionedDir.getName());
            if (maxVersion < version.getValue()) {
                maxVersion = version.getValue();
                maxVersionFile = versionedDir;
            }
        }
        return dir.equals(maxVersionFile);
    }

    private boolean isVersionedDir(File dir) {
        JarVersion version = new JarVersion(dir.getName());
        return version.getValue() > 0.0;
    }

    private void processOneDir(File dir) {
        if (!dir.exists()) {
            log.error("This directory does not exists!!: " + dir.getPath());
            return;
        }
        log.debug("Processing directory: " + dir.getPath());
        final File[] nonFilteredFiles = dir.listFiles();
        //log.debug("non filtered: " + nonFilteredFiles);
        for (File one : nonFilteredFiles) {
            if (one.isDirectory()) {
                if (!".".equals(one.getName()) && !"..".equals(one.getName())) {
                    if (isVersionedDir(one)) {
                        log.debug("This is versioned dir: " + one.getName());
                        if (isLastVersionDir(one)) {
                            log.debug("LAST version detected.");
                            processOneDir(one);
                        } else {
                            log.debug("..it's NOT last version.");
                        }
                    } else {
                        processOneDir(one);
                    }
                }
            } else {
                //log.debug("is file: " + one.getName());
                if (isFileOk(one)) {
                    log.debug("OK: " + one.getName());
                    onFileFound(one);
                } else {
                    //log.debug("File is not OK: " + one.getName());
                }
            }
        }
    }


    public void addListener(FileActionListener listener) {
        listeners.add(listener);
    }

}
